#ifndef HEADER_HPP
#define HEADER_HPP 1

#include <iostream>
#include <fstream>
#include <cstdint>
#include <boost/crc.hpp>
#include "exception.hpp"

namespace fur
{
   enum class HeadType
   {
      ALL = 0,
      MARK = 0x72,
      MAIN = 0x73,
      FILE = 0x74,
      COMM = 0x75,
      AV = 0x76,
      SUB = 0x77,
      PROTECT = 0x78,
      SIGN = 0x79,
      NEWSUB = 0x7a,
      END = 0x7b
   }; 

   enum class HostSystem
   {
      DOS = 0,
      OS2 = 1,
      W32 = 2,
      UNIX = 3,
      MACOS = 4,
      BEOS = 5,
      MAX = 6
   };

   class BaseHead
   {
      friend void read_head(BaseHead *&head, std::ifstream &file) throw(Exception);
   public:
      BaseHead(const BaseHead &basehead) throw();
      BaseHead() throw() {};
      HeadType get_headtype() const throw()
      { return headtype; };
      uint16_t get_headsize() const throw()
      { return headsize; };
   protected:
      void read_base(std::ifstream &file) throw();
      virtual void read_remain(std::ifstream &file) throw(Exception) {};
      uint16_t headcrc;
      HeadType headtype;
      uint16_t flags;
      uint16_t headsize;
      boost::crc_optimal<32, 0x4c11db7, 0xffffffff, 0, true, true> tmpcrc;
   };
   
   class MainHead:public BaseHead
   {
      friend void read_head(BaseHead *&head, std::ifstream &file) throw(Exception);
   protected:
      MainHead(const BaseHead &basehead) throw()
	 :BaseHead(basehead) {};
      void read_remain(std::ifstream &file) throw(Exception);
      uint16_t reserved1;
      uint32_t reserved2;
   };

   class FileHead:public BaseHead
   {
      friend void read_head(BaseHead *&head, std::ifstream &file) throw(Exception);
   public:
      uint32_t get_packsize() const throw()
      { return packsize; };
      uint32_t get_filecrc() const throw()
      { return filecrc; };
      uint8_t get_version() const throw()
      { return version; };
      const char *get_filename() const throw()
      { return filename; };
   protected:
      FileHead(const BaseHead &basehead) throw()
	 :BaseHead(basehead) {};
      void read_remain(std::ifstream &file) throw(Exception);
      uint32_t packsize;
      uint32_t unpacksize;
      uint8_t hostsystem;
      uint32_t filecrc;
      uint32_t filetime;
      uint8_t version;
      uint8_t method;
      uint16_t namesize;
      uint32_t fileattr;
      char filename[1024];
   };

   class EndHead:public BaseHead
   {
      friend void read_head(BaseHead *&head, std::ifstream &file)  throw(Exception);
   protected:
      EndHead(const BaseHead &basehead) throw()
	 :BaseHead(basehead) {};
      void read_remain(std::ifstream &file) throw(Exception);
   };
   
   void read_head(BaseHead *&head,std::ifstream &file) throw(Exception);//head must be allocated by new
   void check_rarhead(std::ifstream &file) throw(Exception);
} // end of namespace fur
#endif /* header.hpp */
